在很早之前我們曾經提過 Rack 這個東西,也提到在 Rack 裡面有一個很特別的 Hash 叫 ENV
,裡面帶有非常多有用的資料,靠著這些資料我們可以來處理與瀏覽器(也就是使用者)之間的互動,而這個互動其實靠的就是 Request 和 Response 來回,今天我們要來處理的就是 Request
在剛建立 Mavericks 時曾經提過 @env
,那時候我們只有印出來看看裡面有哪些東西,還有在處理 routing
的時候,也有用到 env["PATH_INFO"]
,之後我們有一段時間就沒在提起 env
,接下來我們會透過 Rack 提供的 Request
來處理送進網站的資訊,請看以下程式碼所示
# mavericks/lib/mavericks/controller.rb
def request
@request ||= Rack::Request.new(@env)
end
透過 Rack 來 new 一個 Request
物件的同時,我們也把 @env
傳送進去,在這個地方會看到我們使用了像這樣的語法
@request ||= Rack::Request.new(@env)
目的是為了把結果 caches 起來,有很多人常會誤解這樣的語法展開後是這樣
# 這是錯誤的觀念
@request = @request || Rack::Request.new(@env)
但其實應該是這樣
# 這才是對的觀念
@request || @request = Rack::Request.new(@env)
意思是說,如果 @request
是 nil
or false
,才去做 ||
右邊的行為,這樣就不用每次都呼叫 Rack::Request.new(@env) 來花費效能
另外我們也加了一個 method 叫 params
,裡面直接回傳 Rack::Request
已經幫我們處理好的 params
# mavericks/lib/mavericks/controller.rb
def params
request.params
end
整個 Mavericks 的 Controller 現在就會長這樣
# mavericks/lib/mavericks/controller.rb
require 'erubi'
require "mavericks/file_model"
module Mavericks
class Controller
.
.
(略)
def request
@request ||= Rack::Request.new(@env)
end
def params
request.params
end
end
end
接著立刻回到 just_do
,來看看怎麼使用
# just_do/app/controllers/tasks_controller.rb
def show
@task = FileModel.find(params['id'])
end
我們將原本 FileModel.find(1)
改成 FileModel.find(params['id'])
,利用在 Mavericks 處理好的 params
來找尋特定資料,現在打開瀏覽器進入到 http://127.0.0.1:3001/tasks/show?id=1
這頁,會發現我們已經可以透過 request
帶著 params
,也就是 ?id=1
來取得特定的資料,如果有其他 JSON 檔案的話,也可以改變 id
來找尋其他資料
除了 params
以外,也來看看 request
裡面可以拿到什麼資訊
# just_do/app/controllers/tasks_controller.rb
def show
@task = FileModel.find(params['id'])
@user_agent = request.user_agent
end
這裡我們嘗試取得 user_agent
,然後修改一下 View
<!-- just_do/app/views/tasks/show.html.erb -->
<div class="row">
<div class="col-6">
<div class="card">
<div class="card-header">
任務細節
</div>
<div class="card-body">
<div class="row">
<div class="col"><%= @user_agent %></div>
</div>
<div class="row">
<label class="col">任務名稱</label>
<div class="col">
<%= @task['title'] %>
</div>
</div>
<div class="row">
<label class="col">任務內容</label>
<div class="col">
<%= @task['content'] %>
</div>
</div>
</div>
</div>
</div>
</div>
如果沒有出錯的話,會在頁面上看到類似像 Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6)...
這樣的user agent
資訊
其實 Response 在之前我們就處理過了,只是當時我們是用比較 hard-core
的做法,還記得一開始看到的這串陣列嗎?
[200, {'Content-Type' => 'text/html'}, [text]]
但其實我們可以利用 Rack 來「包裝」起來,讓我們先回到 Mavericks 的 Controller
首先我們要將 response 包裝起來,所以要加上一個 response
method,同時建立一個 get_response
來檢查有沒有 response
# mavericks/lib/mavericks/controller.rb
def response(text, status = 200, headers = {})
raise "Already responded!" if @response
@response = Rack::Response.new(text, status, headers)
end
def get_response
@response
end
接著修改 render_layout
,透過這樣的修改,會先經過 response
的包裝來回應網頁內容,而不是直接回傳
# mavericks/lib/mavericks/controller.rb
def render_layout
layout = File.read "app/views/layouts/application.html.erb"
text = eval(Erubi::Engine.new(layout).src)
response(text)
end
接著將 called_render
拿掉,並且在那一行 attr_reader
的 :called_render
也換成 :content
# mavericks/lib/mavericks/controller.rb
# 將 :called_render 換成 :content
attr_reader :env, :content
def initialize(env)
@env = env
# - @called_render = false
@content = nil
end
def render(view_name)
# - @called_render = true
filename = File.join "app", "views", controller_name, "#{view_name}.html.erb"
template = File.read filename
@content = eval(Erubi::Engine.new(template).src)
end
回到 call
做一些小修改,修改的內容為用 controller.content
檢查有沒有主動寫 render
,如果沒有的話,那就呼叫 default_render
# mavericks/lib/mavericks.rb
module Mavericks
class Error < StandardError; end
class Application
def call(env)
return favicon if env["PATH_INFO"] == '/favicon.ico'
return index(env) if env["PATH_INFO"] == '/'
begin
klass, act = get_controller_and_action(env)
controller = klass.new(env)
controller.send(act)
default_render(controller, act) unless controller.content
controller.render_layout
if controller.get_response
controller.get_response.to_a
else
[500, {'Content-Type' => 'text/html'},
['server error!!']]
end
rescue
[404, {'Content-Type' => 'text/html'},
['This is a 404 page!!']]
end
end
# .
# .
# (略)
然後測試看看,因為我們只有處理 Response
的部分,所以畫面應該都不會有什麼更動才對,如果都沒有出錯,代表完成了!
休淡幾勒!我們還沒碰到真正的資料庫耶,這幾天做的 Model 充其量也只是個普通 Model,但根本不是真正的 ORM 呀,明天開始就會進入 Model 比較核心的部分,也就是對資料庫做操作